// __AESL_COPYRIGHT__

#ifndef AESL_COMP_H
#define AESL_COMP_H


#include <systemc>
//#include "systemc.h"

#define DoIfDebug(x) 


///CFU:  combinational FU, no clock, reset and ce
//template<int NUM_STAGE,
//         int DIN0_WIDTH, int DIN1_WIDTH=DIN0_WIDTH,
//         int DOUT_WIDTH=DIN0_WIDTH> 
template<int NUM_STAGE,
         int DIN0_WIDTH, int DIN1_WIDTH,
         int DOUT_WIDTH> class AESLFUComp;
template<int NUM_STAGE,
         int DIN0_WIDTH, int DIN1_WIDTH,
         int DOUT_WIDTH> 
SC_MODULE( AESLCFUComp )
{
  public:
    sc_in< sc_lv<DIN0_WIDTH> > din0;
    sc_in< sc_lv<DIN1_WIDTH> > din1;
    
    sc_out< sc_lv<DOUT_WIDTH> > dout;

    SC_CTOR( AESLCFUComp );
    
    AESLFUComp<NUM_STAGE, DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH> *super;
    
    virtual void sanity_check() {}
    
    virtual sc_lv<DOUT_WIDTH>
        compute(const sc_lv<DIN0_WIDTH>& in0, const sc_lv<DIN1_WIDTH>& in1) 
	{ if (super) return super->compute(in0, in1); 
          else return sc_lv<DOUT_WIDTH>(); }
    
    void thread_compute() {
        DoIfDebug(
        {
            if (DIN0_WIDTH == 64)
                printf("[ACMP_ADD] input: %llx %llx\n",
                       din0.read().to_uint64(), 
                       din1.read().to_uint64());
            else
                printf("[ACMP_ADD] input: %x %x\n",
                       din0.read().to_uint(), 
                       din1.read().to_uint());
        });
        
        sc_lv<DOUT_WIDTH> result = compute(din0.read(), din1.read());
        
        dout.write(result);

        DoIfDebug(
        {
            
            if (DIN0_WIDTH == 64)
                printf("[ACMP_ADD] comb output: %llx\n", result.to_uint64());
            else 
                printf("[ACMP_ADD] comb output: %x\n", result.to_uint());
        });
    }
};


template<int NUM_STAGE,
         int DIN0_WIDTH, int DIN1_WIDTH,
         int DOUT_WIDTH> 
AESLCFUComp<NUM_STAGE, DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>::
AESLCFUComp(const sc_module_name name): sc_module( name ), super(0) {
    //sanity_check();
    
    SC_METHOD( thread_compute );
    sensitive << din0;
    sensitive << din1;
}


///FU:  sequential FU, with clock, reset and ce
template<int NUM_STAGE,
         int DIN0_WIDTH, int DIN1_WIDTH,
         int DOUT_WIDTH> 
SC_MODULE(AESLFUComp)
{
  AESLCFUComp<NUM_STAGE, DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH> core;
  public:
    sc_in< bool > clk;
    sc_in< sc_logic > reset;
    sc_in< sc_logic > ce;
    sc_in< sc_lv<DIN0_WIDTH> > din0;
    sc_in< sc_lv<DIN1_WIDTH> > din1;
    sc_out< sc_lv<DOUT_WIDTH> > dout;

    /// Dont use stage_regvec[0].
    sc_signal< sc_lv<DOUT_WIDTH> > stage_regvec[NUM_STAGE];
    
    SC_CTOR( AESLFUComp );
    
    virtual sc_lv<DOUT_WIDTH>
        compute(const sc_lv<DIN0_WIDTH>& in0, const sc_lv<DIN1_WIDTH>& in1) 
	{ return sc_lv<DOUT_WIDTH>(); }

    void thread_stage() {
        if (ce.read() == SC_LOGIC_0) return;
        
        dout.write(stage_regvec[NUM_STAGE-1]);
        for (unsigned i = NUM_STAGE-1; i > 1; --i) {
            stage_regvec[i].write(stage_regvec[i-1].read());
        }

        DoIfDebug(
        {
            if (DIN0_WIDTH == 64)
                printf("[ACMP_ADD] reg output %llx\n",
                       stage_regvec[NUM_STAGE-1].read().to_uint64());
            else
                printf("[ACMP_ADD] reg output %x\n",
                       stage_regvec[NUM_STAGE-1].read().to_uint());
        });
    }
    
};


template<int NUM_STAGE,
         int DIN0_WIDTH, int DIN1_WIDTH,
         int DOUT_WIDTH> 
AESLFUComp<NUM_STAGE, DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>::
AESLFUComp(const sc_module_name name): sc_module( name ), core("CFU_U") {
    //sanity_check();
    core.din0(din0);
    core.din1(din1);
    core.dout(stage_regvec[1]);
    core.super = this;
    if (NUM_STAGE > 1)
    {
        SC_METHOD( thread_stage );
        sensitive << clk.pos();
    }
}


 

template<int OPC_WIDTH, int DIN_WIDTH, int DOUT_WIDTH> 
struct FUComputeCore 
{

    virtual sc_lv<DOUT_WIDTH>
    compute(const sc_lv<OPC_WIDTH>& opcode, const sc_lv<DIN_WIDTH>& in)=0;
};


////////////////////////////////////////////////////////////////
// Abstract Multi-op combinational Functional Units.
////////////////////////////////////////////////////////////////
template<int NUM_STAGE, int OPC_WIDTH, int DIN_WIDTH, int DOUT_WIDTH> 
SC_MODULE( AESLCFUMOComp )
{
  public:
    sc_in< sc_lv<OPC_WIDTH> > opcode;
    
    sc_in< sc_lv<DIN_WIDTH> > din;
    sc_out< sc_lv<DOUT_WIDTH> > dout;


  public:
    SC_CTOR( AESLCFUMOComp ) {
        assert(OPC_WIDTH > 0 && DIN_WIDTH > 0 && DOUT_WIDTH > 0
               && NUM_STAGE == 1);
        
        SC_METHOD( thread_compute );
        sensitive << opcode << din;
        
    }
    
    //virtual sc_lv<DOUT_WIDTH>
    //compute(const sc_lv<OPC_WIDTH>& opcode, const sc_lv<DIN_WIDTH>& in) {

    FUComputeCore<OPC_WIDTH, DIN_WIDTH, DOUT_WIDTH>* mCore;
    
    void setComputeCore(FUComputeCore<OPC_WIDTH, DIN_WIDTH, DOUT_WIDTH>* core) {
        mCore = core;
    }
    FUComputeCore<OPC_WIDTH, DIN_WIDTH, DOUT_WIDTH>* getComputeCore() {
        return mCore;
    }
    
  public:

    void thread_compute() {
        DoIfDebug( cerr << "[ACMP] " << name() << " Input: "
               << din.read() << endl; );
        assert(mCore);
        
        sc_lv<DOUT_WIDTH> result;
        if (din.read().is_01()) {
            result = mCore->compute(opcode.read(), din.read());
        } 
        else {
            result = sc_lv<DOUT_WIDTH>();
        }

        dout.write(result);

        DoIfDebug( cerr << "[ACMP] " << name() << " C-out: "
               << result << endl;);
    }
    
};


////////////////////////////////////////////////////////////////
// Abstract Pipelined Functional Units.
////////////////////////////////////////////////////////////////
template<int NUM_STAGE, int OPC_WIDTH, int DIN_WIDTH, int DOUT_WIDTH> 
SC_MODULE( AESLFUMultiCycle )
{
  public:
    sc_in< bool > clk;
    sc_in< sc_logic > reset;
    sc_in< sc_logic > ce;
    sc_in< sc_lv<OPC_WIDTH> > opcode;
    
    sc_in< sc_lv<DIN_WIDTH> > din;
    sc_out< sc_lv<DOUT_WIDTH> > dout;


  public:
    SC_CTOR( AESLFUMultiCycle ) {
        assert(OPC_WIDTH > 0 && DIN_WIDTH > 0 && DOUT_WIDTH > 0
               && NUM_STAGE > 0);
        
        SC_METHOD( thread_compute );
        sensitive << opcode << din;
        sensitive << clk;
        
        if (NUM_STAGE > 1)
        {
            SC_METHOD( thread_stage );
            sensitive << (clk.pos());
        }
    }
    
    //virtual sc_lv<DOUT_WIDTH>
    //compute(const sc_lv<OPC_WIDTH>& opcode, const sc_lv<DIN_WIDTH>& in) {

    FUComputeCore<OPC_WIDTH, DIN_WIDTH, DOUT_WIDTH>* mCore;
    
    void setComputeCore(FUComputeCore<OPC_WIDTH, DIN_WIDTH, DOUT_WIDTH>* core) {
        mCore = core;
    }
    FUComputeCore<OPC_WIDTH, DIN_WIDTH, DOUT_WIDTH>* getComputeCore() {
        return mCore;
    }
    
  public:
    sc_signal< sc_lv<DOUT_WIDTH> > stage_regvec[NUM_STAGE];

    void thread_compute() {
        DoIfDebug( cerr << "[ACMP] " << name() << " Input: "
               << din.read() << endl; );
        assert(mCore);
        
        sc_lv<DOUT_WIDTH> result;
        if (din.read().is_01()) {
            result = mCore->compute(opcode.read(), din.read());
        } 
        else {
            result = sc_lv<DOUT_WIDTH>();
        }

        if (NUM_STAGE > 1)
            stage_regvec[1].write(result);
        else
            dout.write(result);

        DoIfDebug( cerr << "[ACMP] " << name() << " C-out: "
               << result << endl;);
    }
    
    void thread_stage() {
        if (ce.read() == SC_LOGIC_0) return;
        
        dout.write(stage_regvec[NUM_STAGE-1]);
        for (unsigned i = NUM_STAGE-1; i > 1; --i) {
            stage_regvec[i].write(stage_regvec[i-1].read());
        }

        DoIfDebug(
            cerr << "[ACMP] " << name() << " R-out: "
            << stage_regvec[NUM_STAGE-1].read() << endl;);
    }
};



////////////////////////////////////////////////////////////////
/// Unary operator
////////////////////////////////////////////////////////////////
template< int NUM_STAGE,
          int DIN0_WIDTH, 
          int DOUT_WIDTH=DIN0_WIDTH> 
SC_MODULE( CFUUnaryOp )
{
  public:
    sc_in< sc_lv<DIN0_WIDTH> > din0;
    sc_out< sc_lv<DOUT_WIDTH> > dout;
    
    SC_CTOR(CFUUnaryOp) {
        mMCModule =
            new AESLFUMultiCycle
            <NUM_STAGE, 1, DIN0_WIDTH, DOUT_WIDTH>("u_U");

        mMCModule->opcode(sigone);
        mMCModule->din(din0);
        mMCModule->dout(dout);
        mMCModule->clk(clk);
        mMCModule->reset(reset);
        mMCModule->ce(ce);

        SC_METHOD(IOConnection);
    }
    
    AESLFUMultiCycle<NUM_STAGE, 1, DIN0_WIDTH, DOUT_WIDTH>* mMCModule;

    void setComputeCore(FUComputeCore<1, DIN0_WIDTH, DOUT_WIDTH>* core) {
        mMCModule->setComputeCore(core);
    }

  private:
    sc_signal< sc_lv<1> > sigone;
    sc_signal< bool > clk;
    sc_signal< sc_logic > reset;
    sc_signal< sc_logic > ce;

    void IOConnection() {
        sigone.write(sc_lv<1>("1"));
    }

};

template< int NUM_STAGE,
          int DIN0_WIDTH, 
          int DOUT_WIDTH=DIN0_WIDTH> 
SC_MODULE( FUUnaryOp )
{
  public:
    sc_in< bool > clk;
    sc_in< sc_logic > reset;
    sc_in< sc_logic > ce;
    sc_in< sc_lv<DIN0_WIDTH> > din0;
    sc_out< sc_lv<DOUT_WIDTH> > dout;
    
    SC_CTOR(FUUnaryOp) {
        mMCModule =
            new AESLFUMultiCycle
            <NUM_STAGE, 1, DIN0_WIDTH, DOUT_WIDTH>("u_U");

        mMCModule->opcode(sigone);
        mMCModule->din(din0);
        mMCModule->dout(dout);
        mMCModule->clk(clk);
        mMCModule->reset(reset);
        mMCModule->ce(ce);

        SC_METHOD(IOConnection);
        sensitive << clk;
    }
    
    AESLFUMultiCycle<NUM_STAGE, 1, DIN0_WIDTH, DOUT_WIDTH>* mMCModule;

    void setComputeCore(FUComputeCore<1, DIN0_WIDTH, DOUT_WIDTH>* core) {
        mMCModule->setComputeCore(core);
    }

  private:
    sc_signal< sc_lv<1> > sigone;

    void IOConnection() {
        sigone.write(sc_lv<1>("1"));
    }

};


template< int NUM_STAGE,
          int OPC_WIDTH,
          int DIN0_WIDTH, 
          int DOUT_WIDTH> 
SC_MODULE( FUMOUnaryOp )
{
  public:
    sc_in< bool > clk;
    sc_in< sc_logic > reset;
    sc_in< sc_logic > ce;
    sc_in< sc_lv<OPC_WIDTH> > opcode;
    sc_in< sc_lv<DIN0_WIDTH> > din0;
    sc_out< sc_lv<DOUT_WIDTH> > dout;
    
    SC_CTOR(FUMOUnaryOp) {
        mMCModule =
            new AESLFUMultiCycle
            <NUM_STAGE, OPC_WIDTH, DIN0_WIDTH, DOUT_WIDTH>("mu_U");

        mMCModule->opcode(opcode);
        mMCModule->din(din0);
        mMCModule->dout(dout);
        mMCModule->clk(clk);
        mMCModule->reset(reset);
        mMCModule->ce(ce);
    }

    AESLFUMultiCycle<NUM_STAGE, OPC_WIDTH, DIN0_WIDTH, DOUT_WIDTH>* mMCModule;
    
    void setComputeCore(FUComputeCore<OPC_WIDTH, DIN0_WIDTH, DOUT_WIDTH>* core) {
        mMCModule->setComputeCore(core);
    }
};

template< int NUM_STAGE,
          int DIN0_WIDTH,
          int DIN1_WIDTH,
          int DOUT_WIDTH> 
SC_MODULE (CFUBinaryOp)
{
  public:
    sc_in< sc_lv<DIN0_WIDTH> > din0;
    sc_in< sc_lv<DIN1_WIDTH> > din1;
    sc_out< sc_lv<DOUT_WIDTH> > dout;

    AESLFUMultiCycle<NUM_STAGE, 1, DIN0_WIDTH+DIN1_WIDTH, DOUT_WIDTH>*
        mMCModule;

    SC_CTOR( CFUBinaryOp ) {
        mMCModule =
            new AESLFUMultiCycle
            <NUM_STAGE, 1, DIN0_WIDTH+DIN1_WIDTH, DOUT_WIDTH>("b_u");
        //sigone.write(sc_lv<1>("1"));

        mMCModule->opcode(sigone);
        mMCModule->din(din_sig);
        mMCModule->dout(dout);
        mMCModule->clk(clk);
        mMCModule->reset(reset);
        mMCModule->ce(ce);
        
        SC_METHOD(IOConnection);
        sensitive << din0 << din1;
    }
    
    void setComputeCore(FUComputeCore<1, DIN0_WIDTH+DIN1_WIDTH, DOUT_WIDTH>*
                        core) {
        mMCModule->setComputeCore(core);
    }

    sc_signal< sc_lv<DIN0_WIDTH+DIN1_WIDTH> > din_sig;
    sc_signal< sc_lv<1> > sigone;
    sc_signal< bool > clk;
    sc_signal< sc_logic > reset;
    sc_signal< sc_logic > ce;
    
    void IOConnection() {
        sigone.write(sc_lv<1>("1"));
        sc_lv<DIN0_WIDTH+DIN1_WIDTH> din_tmp;
        din_tmp.range(0, DIN0_WIDTH-1) =
            din0.read().range(0, DIN0_WIDTH-1);
        din_tmp.range(DIN0_WIDTH, DIN0_WIDTH+DIN1_WIDTH-1) =
            din1.read().range(0, DIN1_WIDTH-1);
        din_sig.write(din_tmp);
    }
};



template< int NUM_STAGE,
          int DIN0_WIDTH,
          int DIN1_WIDTH,
          int DOUT_WIDTH> 
SC_MODULE (FUBinaryOp)
{
  public:
    sc_in< bool > clk;
    sc_in< sc_logic > reset;
    sc_in< sc_logic > ce;
    sc_in< sc_lv<DIN0_WIDTH> > din0;
    sc_in< sc_lv<DIN1_WIDTH> > din1;
    sc_out< sc_lv<DOUT_WIDTH> > dout;

    AESLFUMultiCycle<NUM_STAGE, 1, DIN0_WIDTH+DIN1_WIDTH, DOUT_WIDTH>*
        mMCModule;

    SC_CTOR( FUBinaryOp ) {
        mMCModule =
            new AESLFUMultiCycle
            <NUM_STAGE, 1, DIN0_WIDTH+DIN1_WIDTH, DOUT_WIDTH>("b_u");
        //sigone.write(sc_lv<1>("1"));

        mMCModule->opcode(sigone);
        mMCModule->din(din_sig);
        mMCModule->dout(dout);
        mMCModule->clk(clk);
        mMCModule->reset(reset);
        mMCModule->ce(ce);
        
        SC_METHOD(IOConnection);
        sensitive << din0 << din1 << clk;
    }
    
    void setComputeCore(FUComputeCore<1, DIN0_WIDTH+DIN1_WIDTH, DOUT_WIDTH>*
                        core) {
        mMCModule->setComputeCore(core);
    }

    sc_signal< sc_lv<DIN0_WIDTH+DIN1_WIDTH> > din_sig;
    sc_signal< sc_lv<1> > sigone;
    
    void IOConnection() {
        sigone.write(sc_lv<1>("1"));
        sc_lv<DIN0_WIDTH+DIN1_WIDTH> din_tmp;
        din_tmp.range(0, DIN0_WIDTH-1) =
            din0.read().range(0, DIN0_WIDTH-1);
        din_tmp.range(DIN0_WIDTH, DIN0_WIDTH+DIN1_WIDTH-1) =
            din1.read().range(0, DIN1_WIDTH-1);
        din_sig.write(din_tmp);
    }
};

template< int NUM_STAGE,
          int OPC_WIDTH, 
          int DIN0_WIDTH,
          int DIN1_WIDTH=DIN0_WIDTH,
          int DOUT_WIDTH=DIN0_WIDTH> 
SC_MODULE (CFUMOBinaryOp)
{
  public:
    sc_in< sc_lv<OPC_WIDTH> > opcode;
    sc_in< sc_lv<DIN0_WIDTH> > din0;
    sc_in< sc_lv<DIN1_WIDTH> > din1;
    sc_out< sc_lv<DOUT_WIDTH> > dout;

    AESLCFUMOComp<NUM_STAGE, OPC_WIDTH, DIN0_WIDTH+DIN1_WIDTH, DOUT_WIDTH>*
        mMCModule;

    SC_CTOR( CFUMOBinaryOp ) {
        mMCModule =
            new AESLCFUMOComp
            <NUM_STAGE, OPC_WIDTH, DIN0_WIDTH+DIN1_WIDTH, DOUT_WIDTH>("mb_U");

        mMCModule->opcode(opcode);
        mMCModule->din(din_sig);
        mMCModule->dout(dout);
        
        SC_METHOD(IOConnection);
        sensitive << din0 << din1 << opcode ;
    }
    
    void setComputeCore(FUComputeCore<OPC_WIDTH, DIN0_WIDTH+DIN1_WIDTH, DOUT_WIDTH>* core) {
        mMCModule->setComputeCore(core);
    }

    sc_signal< sc_lv<DIN0_WIDTH+DIN1_WIDTH> > din_sig;
    
    void IOConnection() {
        sc_lv<DIN0_WIDTH+DIN1_WIDTH> din_tmp;
        din_tmp.range(0, DIN0_WIDTH-1) =
            din0.read().range(0, DIN0_WIDTH-1);
        din_tmp.range(DIN0_WIDTH, DIN0_WIDTH+DIN1_WIDTH-1) =
            din1.read().range(0, DIN1_WIDTH-1);
        din_sig.write(din_tmp);
    }
};



template< int NUM_STAGE,
          int OPC_WIDTH, 
          int DIN0_WIDTH,
          int DIN1_WIDTH=DIN0_WIDTH,
          int DOUT_WIDTH=DIN0_WIDTH> 
SC_MODULE (FUMOBinaryOp)
{
  public:
    sc_in< bool > clk;
    sc_in< sc_logic > reset;
    sc_in< sc_logic > ce;
    sc_in< sc_lv<OPC_WIDTH> > opcode;
    sc_in< sc_lv<DIN0_WIDTH> > din0;
    sc_in< sc_lv<DIN1_WIDTH> > din1;
    sc_out< sc_lv<DOUT_WIDTH> > dout;

    AESLFUMultiCycle<NUM_STAGE, OPC_WIDTH, DIN0_WIDTH+DIN1_WIDTH, DOUT_WIDTH>*
        mMCModule;

    SC_CTOR( FUMOBinaryOp ) {
        mMCModule =
            new AESLFUMultiCycle
            <NUM_STAGE, OPC_WIDTH, DIN0_WIDTH+DIN1_WIDTH, DOUT_WIDTH>("mb_U");

        mMCModule->opcode(opcode);
        mMCModule->din(din_sig);
        mMCModule->dout(dout);
        mMCModule->clk(clk);
        mMCModule->reset(reset);
        mMCModule->ce(ce);
        
        SC_METHOD(IOConnection);
        sensitive << din0 << din1 << opcode << clk;
    }
    
    void setComputeCore(FUComputeCore<OPC_WIDTH, DIN0_WIDTH+DIN1_WIDTH, DOUT_WIDTH>* core) {
        mMCModule->setComputeCore(core);
    }

    sc_signal< sc_lv<DIN0_WIDTH+DIN1_WIDTH> > din_sig;
    
    void IOConnection() {
        sc_lv<DIN0_WIDTH+DIN1_WIDTH> din_tmp;
        din_tmp.range(0, DIN0_WIDTH-1) =
            din0.read().range(0, DIN0_WIDTH-1);
        din_tmp.range(DIN0_WIDTH, DIN0_WIDTH+DIN1_WIDTH-1) =
            din1.read().range(0, DIN1_WIDTH-1);
        din_sig.write(din_tmp);
    }
};


template< bool SIGNED,
          int DIN0_W,
          int DIN1_W=DIN0_W,
          int DOUT_W=DIN0_W> 
struct IntArithCoreDivRem :
    public FUComputeCore<2, DIN0_W+DIN1_W, DOUT_W>
{
    virtual sc_lv<DOUT_W>
    compute(const sc_lv<2>& opcode, const sc_lv<DIN0_W+DIN1_W>& in) {
        sc_lv<DIN0_W> in0;
        sc_lv<DIN1_W> in1;
        (in1, in0) = in;

        sc_lv<DOUT_W> undef;
    	if (!opcode.is_01() || !in.is_01()) return undef;
        bool opc = opcode[0].to_bool();

        if (!SIGNED) {
            unsigned long long val0 = in0.to_uint64();
            unsigned long long val1 = in1.to_uint64();
            if (val1 == 0) return undef;
            unsigned long long outv = opc ? (val0 % val1) : (val0 / val1);
            return sc_lv<DOUT_W>(outv);
        } else {
            long long val0 = in0.to_int64();
            long long val1 = in1.to_int64();
            if (val1 == 0) return undef;
            long long outv = opc ? (val0 % val1) : (val0 / val1);
            return sc_lv<DOUT_W>(outv);
        }
    }
};
    
////////////////////////////////////////////////////////////////
// Integer UDIV/UREM component.
////////////////////////////////////////////////////////////////
template<int ID,
         int NUM_STAGE,
         int DIN0_W,
         int DIN1_W,
         int DOUT_W> 
struct ACMP_udivurem_comb :
    public CFUMOBinaryOp<NUM_STAGE, 2, DIN0_W, DIN1_W, DOUT_W>
{

    ACMP_udivurem_comb(const char* mname ) :
        CFUMOBinaryOp<NUM_STAGE, 2, DIN0_W, DIN1_W, DOUT_W>(mname) {
        setComputeCore(
            new
            IntArithCoreDivRem<false, DIN0_W, DIN1_W, DOUT_W>());
    }
};


template<int ID,
         int NUM_STAGE,
         int DIN0_W,
         int DIN1_W,
         int DOUT_W> 
struct ACMP_udivurem :
    public FUMOBinaryOp<NUM_STAGE, 2, DIN0_W, DIN1_W, DOUT_W>
{

    ACMP_udivurem(const char* mname ) :
        FUMOBinaryOp<NUM_STAGE, 2, DIN0_W, DIN1_W, DOUT_W>(mname) {
        setComputeCore(
            new
            IntArithCoreDivRem<false, DIN0_W, DIN1_W, DOUT_W>());
    }
};



////////////////////////////////////////////////////////////////
// Integer SDIV/SREM component.
////////////////////////////////////////////////////////////////
template<int ID,
         int NUM_STAGE,
         int DIN0_W,
         int DIN1_W,
         int DOUT_W> 
struct ACMP_sdivsrem_comb :
    public CFUMOBinaryOp<NUM_STAGE, 2, DIN0_W, DIN1_W, DOUT_W>
{

    ACMP_sdivsrem_comb(const char* mname ) :
        CFUMOBinaryOp<NUM_STAGE, 2, DIN0_W, DIN1_W, DOUT_W>(mname) {
        setComputeCore(
            new IntArithCoreDivRem<true, DIN0_W, DIN1_W, DOUT_W>());
    }
};


template<int ID,
         int NUM_STAGE,
         int DIN0_W,
         int DIN1_W,
         int DOUT_W> 
struct ACMP_sdivsrem :
    public FUMOBinaryOp<NUM_STAGE, 2, DIN0_W, DIN1_W, DOUT_W>
{

    ACMP_sdivsrem(const char* mname ) :
        FUMOBinaryOp<NUM_STAGE, 2, DIN0_W, DIN1_W, DOUT_W>(mname) {
        setComputeCore(
            new IntArithCoreDivRem<true, DIN0_W, DIN1_W, DOUT_W>());
    }
};



////////////////////////////////////////////////////////////////
// Integer UDIV component.
////////////////////////////////////////////////////////////////
template<int ID, int NUM_STAGE,
         int DIN0_WIDTH, int DIN1_WIDTH=DIN0_WIDTH,
         int DOUT_WIDTH=DIN0_WIDTH> 
struct ACMP_udiv_comb :
    public AESLCFUComp<NUM_STAGE, DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>
{
public:

    ACMP_udiv_comb(const char* mname) :
        AESLCFUComp<NUM_STAGE, DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>(mname) {
        sanity_check();
    }
         
    virtual void sanity_check() {
        // assert(DIN0_WIDTH == 32);
        // assert(DIN0_WIDTH == DOUT_WIDTH);
    }
    
    virtual sc_lv<DOUT_WIDTH>
    compute(const sc_lv<DIN0_WIDTH>& in0, const sc_lv<DIN1_WIDTH>& in1) {
    	if (!in0.is_01() || !in1.is_01()) return sc_lv<DOUT_WIDTH>();
        unsigned long long ival0 = (in0).to_uint64();
        unsigned long long ival1 = (in1).to_uint64();
            
        unsigned long long outi;
        if (ival1 != 0)  {
            outi = ival0 / ival1;
        } 
        else {
            outi = 0xFFFFFFFFFFFFFFFFULL;
        }

        return sc_lv<DOUT_WIDTH>(outi);
    }

};


template<int ID, int NUM_STAGE,
         int DIN0_WIDTH, int DIN1_WIDTH=DIN0_WIDTH,
         int DOUT_WIDTH=DIN0_WIDTH> 
struct ACMP_udiv :
    public AESLFUComp<NUM_STAGE, DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>
{
public:

    ACMP_udiv(const char* mname) :
        AESLFUComp<NUM_STAGE, DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>(mname) {
        sanity_check();
    }
         
    virtual void sanity_check() {
        // assert(DIN0_WIDTH == 32);
        // assert(DIN0_WIDTH == DOUT_WIDTH);
    }
    
    virtual sc_lv<DOUT_WIDTH>
    compute(const sc_lv<DIN0_WIDTH>& in0, const sc_lv<DIN1_WIDTH>& in1) {
    	if (!in0.is_01() || !in1.is_01()) return sc_lv<DOUT_WIDTH>();
        unsigned long long ival0 = (in0).to_uint64();
        unsigned long long ival1 = (in1).to_uint64();
            
        unsigned long long outi;
        if (ival1 != 0)  {
            outi = ival0 / ival1;
        } 
        else {
            outi = 0xFFFFFFFFFFFFFFFFULL;
        }

        return sc_lv<DOUT_WIDTH>(outi);
    }

};

template<int ID, int NUM_STAGE,
         int DIN0_WIDTH, int DIN1_WIDTH=DIN0_WIDTH,
         int DOUT_WIDTH=DIN0_WIDTH> 
struct ACMP_sdiv_comb :
    public AESLCFUComp<NUM_STAGE, DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>
{
public:

    ACMP_sdiv_comb(const char* mname) :
        AESLCFUComp<NUM_STAGE, DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>(mname) {
        sanity_check();
    }
         
    virtual void sanity_check() {
        //assert(DIN0_WIDTH == 32);
        //assert(DIN0_WIDTH == DOUT_WIDTH);
    }
    
    virtual sc_lv<DOUT_WIDTH>
    compute(const sc_lv<DIN0_WIDTH>& in0, const sc_lv<DIN1_WIDTH>& in1) {
    	if (!in0.is_01() || !in1.is_01()) return sc_lv<DOUT_WIDTH>();
        if (DIN0_WIDTH > 32)
        {
            long long ival0 = (in0).to_uint64();
            long long ival1 = (in1).to_uint64();
            
            long long outi;
            if (ival1 != 0)  {
                outi = ival0 / ival1;
            } 
            else {
                outi = 0x7FFFFFFFFFFFFFFFULL;
            }
            return sc_lv<DOUT_WIDTH>(outi);
        } else {
            int ival0 = (in0).to_int();
            int ival1 = (in1).to_int();
                
            int outi;
            if (ival1 != 0)  {
                outi = ival0 / ival1;
            } 
            else {
                outi = 0x7FFFFFFF;
            }
            return sc_lv<DOUT_WIDTH>(outi);
        }

    }

};


template<int ID, int NUM_STAGE,
         int DIN0_WIDTH, int DIN1_WIDTH=DIN0_WIDTH,
         int DOUT_WIDTH=DIN0_WIDTH> 
struct ACMP_sdiv :
    public AESLFUComp<NUM_STAGE, DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>
{
public:

    ACMP_sdiv(const char* mname) :
        AESLFUComp<NUM_STAGE, DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>(mname) {
        sanity_check();
    }
         
    virtual void sanity_check() {
        //assert(DIN0_WIDTH == 32);
        //assert(DIN0_WIDTH == DOUT_WIDTH);
    }
    
    virtual sc_lv<DOUT_WIDTH>
    compute(const sc_lv<DIN0_WIDTH>& in0, const sc_lv<DIN1_WIDTH>& in1) {
    	if (!in0.is_01() || !in1.is_01()) return sc_lv<DOUT_WIDTH>();
        if (DIN0_WIDTH > 32)
        {
            long long ival0 = (in0).to_uint64();
            long long ival1 = (in1).to_uint64();
            
            long long outi;
            if (ival1 != 0)  {
                outi = ival0 / ival1;
            } 
            else {
                outi = 0x7FFFFFFFFFFFFFFFULL;
            }
            return sc_lv<DOUT_WIDTH>(outi);
        } else {
            int ival0 = (in0).to_int();
            int ival1 = (in1).to_int();
            
            int outi;
            if (ival1 != 0)  {
                outi = ival0 / ival1;
            } 
            else {
                outi = 0x7FFFFFFFULL;
            }
            return sc_lv<DOUT_WIDTH>(outi);
        }

    }

};


template<int DIN0_WIDTH, int DIN1_WIDTH, int DOUT_WIDTH> 
sc_lv<DOUT_WIDTH> compute_mul_ss(const sc_lv<DIN0_WIDTH>& in0, const sc_lv<DIN1_WIDTH>& in1) 
{
    //assert(DIN0_WIDTH <= 64);
    //assert(DIN1_WIDTH <= 64);
    //assert(DOUT_WIDTH <= 64);
    if (!in0.is_01() || !in1.is_01()) return sc_lv<DOUT_WIDTH>();

#if 0
    long long ival0 = (in0).to_int64();
    long long ival1 = (in1).to_int64();
        
    long long outi = ival0 * ival1;
#else
    sc_bigint<DOUT_WIDTH> outi = sc_bigint<DIN0_WIDTH>(in0) * sc_bigint<DIN1_WIDTH>(in1);
#endif    
    return sc_lv<DOUT_WIDTH>(outi);
}

template<int DIN0_WIDTH, int DIN1_WIDTH, int DOUT_WIDTH> 
sc_lv<DOUT_WIDTH> compute_mul_su(const sc_lv<DIN0_WIDTH>& in0, const sc_lv<DIN1_WIDTH>& in1) 
{
    //assert(DIN0_WIDTH <= 64);
    //assert(DIN1_WIDTH <= 64);
    //assert(DOUT_WIDTH <= 64);
    if (!in0.is_01() || !in1.is_01()) return sc_lv<DOUT_WIDTH>();

#if 0
    long long ival0 = (in0).to_int64();
    
    long long ival1;
    if (DIN1_WIDTH == 64) {
        ival1 = (in1).to_uint64();
    } else {
        sc_lv<DIN1_WIDTH + 1> tmplv = (SC_LOGIC_0, in1);
        ival1 = (tmplv).to_int64();
    }
        
    long long outi = ival0 * ival1;
#else
    sc_bigint<DOUT_WIDTH> outi = sc_bigint<DIN0_WIDTH>(in0) * sc_biguint<DIN1_WIDTH>(in1);
#endif
    return sc_lv<DOUT_WIDTH>(outi);
}


template<int DIN0_WIDTH, int DIN1_WIDTH, int DOUT_WIDTH> 
sc_lv<DOUT_WIDTH> compute_mul_us(const sc_lv<DIN0_WIDTH>& in0, const sc_lv<DIN1_WIDTH>& in1) 
{
    //assert(DIN0_WIDTH <= 64);
    //assert(DIN1_WIDTH <= 64);
    //assert(DOUT_WIDTH <= 64);
    if (!in0.is_01() || !in1.is_01()) return sc_lv<DOUT_WIDTH>();

#if 0
    long long ival0;
    if (DIN0_WIDTH == 64) {
        ival0 = (in0).to_uint64();
    } else {
        sc_lv<DIN0_WIDTH + 1> tmplv = (SC_LOGIC_0, in0);
        ival0 = (tmplv).to_int64();
    }
    long long ival1 = (in1).to_int64();
        
    long long outi = ival0 * ival1;
#else
    sc_bigint<DOUT_WIDTH> outi = sc_biguint<DIN0_WIDTH>(in0) * sc_bigint<DIN1_WIDTH>(in1);
#endif
    
    return sc_lv<DOUT_WIDTH>(outi);
}


template<int DIN0_WIDTH, int DIN1_WIDTH, int DOUT_WIDTH> 
sc_lv<DOUT_WIDTH> compute_mul_uu(const sc_lv<DIN0_WIDTH>& in0, const sc_lv<DIN1_WIDTH>& in1) 
{
    //assert(DIN0_WIDTH <= 64);
    //assert(DIN1_WIDTH <= 64);
    //assert(DOUT_WIDTH <= 64);
    if (!in0.is_01() || !in1.is_01()) return sc_lv<DOUT_WIDTH>();

#if 0
    long long ival0;
    if (DIN0_WIDTH == 64) {
        ival0 = (in0).to_uint64();
    } else {
        sc_lv<DIN0_WIDTH + 1> tmplv0 = (SC_LOGIC_0, in0);
        ival0 = (tmplv0).to_int64();
    }
    long long ival1;
    if (DIN1_WIDTH == 64) {
        ival1 = (in1).to_uint64();
    } else {
        sc_lv<DIN1_WIDTH + 1> tmplv1 = (SC_LOGIC_0, in1);
        ival1 = (tmplv1).to_int64();
    }

    long long outi = ival0 * ival1;
#else
    sc_biguint<DOUT_WIDTH> outi = sc_biguint<DIN0_WIDTH>(in0) * sc_biguint<DIN1_WIDTH>(in1);
#endif
    
    return sc_lv<DOUT_WIDTH>(outi);
}


template<int ID, int NUM_STAGE,
         int DIN0_WIDTH, int DIN1_WIDTH=DIN0_WIDTH,
         int DOUT_WIDTH=DIN0_WIDTH> 
struct ACMP_mul_ss :
    public AESLFUComp<NUM_STAGE, DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>
{
public:

    ACMP_mul_ss(const char* mname) :
        AESLFUComp<NUM_STAGE, DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>(mname) {
        sanity_check();
    }
         
    virtual void sanity_check() {
        //assert(DIN0_WIDTH == 32);
        //assert(DIN0_WIDTH == DOUT_WIDTH);
    }
    
    virtual sc_lv<DOUT_WIDTH>
    compute(const sc_lv<DIN0_WIDTH>& in0, const sc_lv<DIN1_WIDTH>& in1) {
        return compute_mul_ss<DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>(in0, in1);
    }

};

template<int ID, int NUM_STAGE,
         int DIN0_WIDTH, int DIN1_WIDTH=DIN0_WIDTH,
         int DOUT_WIDTH=DIN0_WIDTH> 
struct ACMP_mul_su :
    public AESLFUComp<NUM_STAGE, DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>
{
public:

    ACMP_mul_su(const char* mname) :
        AESLFUComp<NUM_STAGE, DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>(mname) {
        sanity_check();
    }
         
    virtual void sanity_check() {
        //assert(DIN0_WIDTH == 32);
        //assert(DIN0_WIDTH == DOUT_WIDTH);
    }
    
    virtual sc_lv<DOUT_WIDTH>
    compute(const sc_lv<DIN0_WIDTH>& in0, const sc_lv<DIN1_WIDTH>& in1) {
        return compute_mul_su<DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>(in0, in1);
    }

};


template<int ID, int NUM_STAGE,
         int DIN0_WIDTH, int DIN1_WIDTH=DIN0_WIDTH,
         int DOUT_WIDTH=DIN0_WIDTH> 
struct ACMP_mul_us :
    public AESLFUComp<NUM_STAGE, DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>
{
public:

    ACMP_mul_us(const char* mname) :
        AESLFUComp<NUM_STAGE, DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>(mname) {
        sanity_check();
    }
         
    virtual void sanity_check() {
        //assert(DIN0_WIDTH == 32);
        //assert(DIN0_WIDTH == DOUT_WIDTH);
    }
    
    virtual sc_lv<DOUT_WIDTH>
    compute(const sc_lv<DIN0_WIDTH>& in0, const sc_lv<DIN1_WIDTH>& in1) {
        return compute_mul_us<DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>(in0, in1);
    }

};


template<int ID, int NUM_STAGE,
         int DIN0_WIDTH, int DIN1_WIDTH=DIN0_WIDTH,
         int DOUT_WIDTH=DIN0_WIDTH> 
struct ACMP_mul_uu :
    public AESLFUComp<NUM_STAGE, DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>
{
public:

    ACMP_mul_uu(const char* mname) :
        AESLFUComp<NUM_STAGE, DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>(mname) {
        sanity_check();
    }
         
    virtual void sanity_check() {
        //assert(DIN0_WIDTH == 32);
        //assert(DIN0_WIDTH == DOUT_WIDTH);
    }
    
    virtual sc_lv<DOUT_WIDTH>
    compute(const sc_lv<DIN0_WIDTH>& in0, const sc_lv<DIN1_WIDTH>& in1) {
        return compute_mul_uu<DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>(in0, in1);
    }
};


template<int ID, int NUM_STAGE,
         int DIN0_WIDTH, int DIN1_WIDTH=DIN0_WIDTH,
         int DOUT_WIDTH=DIN0_WIDTH> 
struct ACMP_smul_ss :
    public AESLCFUComp<NUM_STAGE, DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>
{
public:

    ACMP_smul_ss(const char* mname) :
        AESLCFUComp<NUM_STAGE, DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>(mname) {
        sanity_check();
    }
         
    virtual void sanity_check() {
    }
    
    virtual sc_lv<DOUT_WIDTH>
    compute(const sc_lv<DIN0_WIDTH>& in0, const sc_lv<DIN1_WIDTH>& in1) {
        return compute_mul_ss<DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>(in0, in1);
    }

};

template<int ID, int NUM_STAGE,
         int DIN0_WIDTH, int DIN1_WIDTH=DIN0_WIDTH,
         int DOUT_WIDTH=DIN0_WIDTH> 
struct ACMP_smul_su :
    public AESLCFUComp<NUM_STAGE, DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>
{
public:

    ACMP_smul_su(const char* mname) :
        AESLCFUComp<NUM_STAGE, DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>(mname) {
        sanity_check();
    }
         
    virtual void sanity_check() {
        //assert(DIN0_WIDTH == 32);
        //assert(DIN0_WIDTH == DOUT_WIDTH);
    }
    
    virtual sc_lv<DOUT_WIDTH>
    compute(const sc_lv<DIN0_WIDTH>& in0, const sc_lv<DIN1_WIDTH>& in1) {
        return compute_mul_su<DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>(in0, in1);
    }

};


template<int ID, int NUM_STAGE,
         int DIN0_WIDTH, int DIN1_WIDTH=DIN0_WIDTH,
         int DOUT_WIDTH=DIN0_WIDTH> 
struct ACMP_smul_us :
    public AESLCFUComp<NUM_STAGE, DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>
{
public:

    ACMP_smul_us(const char* mname) :
        AESLCFUComp<NUM_STAGE, DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>(mname) {
        sanity_check();
    }
         
    virtual void sanity_check() {
        //assert(DIN0_WIDTH == 32);
        //assert(DIN0_WIDTH == DOUT_WIDTH);
    }
    
    virtual sc_lv<DOUT_WIDTH>
    compute(const sc_lv<DIN0_WIDTH>& in0, const sc_lv<DIN1_WIDTH>& in1) {
        return compute_mul_us<DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>(in0, in1);
    }

};


template<int ID, int NUM_STAGE,
         int DIN0_WIDTH, int DIN1_WIDTH=DIN0_WIDTH,
         int DOUT_WIDTH=DIN0_WIDTH> 
struct ACMP_smul_uu :
    public AESLCFUComp<NUM_STAGE, DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>
{
public:

    ACMP_smul_uu(const char* mname) :
        AESLCFUComp<NUM_STAGE, DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>(mname) {
        sanity_check();
    }
         
    virtual void sanity_check() {
        //assert(DIN0_WIDTH == 32);
        //assert(DIN0_WIDTH == DOUT_WIDTH);
    }
    
    virtual sc_lv<DOUT_WIDTH>
    compute(const sc_lv<DIN0_WIDTH>& in0, const sc_lv<DIN1_WIDTH>& in1) {
        return compute_mul_uu<DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>(in0, in1);
    }
};

template<int ID, int NUM_STAGE,
         int DIN0_WIDTH, int DIN1_WIDTH=DIN0_WIDTH,
         int DOUT_WIDTH=DIN0_WIDTH> 
struct ACMP_add_comb :
    public AESLCFUComp<NUM_STAGE, DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>
{
public:

    ACMP_add_comb(const char* mname) :
        AESLCFUComp<NUM_STAGE, DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>(mname) {
        sanity_check();
    }
         
    virtual void sanity_check() {
        //assert(DIN0_WIDTH == 32);
        //assert(DIN0_WIDTH == DOUT_WIDTH);
    }
    
    virtual sc_lv<DOUT_WIDTH>
    compute(const sc_lv<DIN0_WIDTH>& in0, const sc_lv<DIN1_WIDTH>& in1) {
        int ival0 = (in0).to_int();
        int ival1 = (in1).to_int();
            
        int outi = ival0 + ival1;

        return sc_lv<DOUT_WIDTH>(outi);
    }

};


template<int ID, int NUM_STAGE,
         int DIN0_WIDTH, int DIN1_WIDTH=DIN0_WIDTH,
         int DOUT_WIDTH=DIN0_WIDTH> 
struct ACMP_add :
    public AESLFUComp<NUM_STAGE, DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>
{
public:

    ACMP_add(const char* mname) :
        AESLFUComp<NUM_STAGE, DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>(mname) {
        sanity_check();
    }
         
    virtual void sanity_check() {
        //assert(DIN0_WIDTH == 32);
        //assert(DIN0_WIDTH == DOUT_WIDTH);
    }
    
    virtual sc_lv<DOUT_WIDTH>
    compute(const sc_lv<DIN0_WIDTH>& in0, const sc_lv<DIN1_WIDTH>& in1) {
       if(in0.is_01() && in1.is_01())
       {
        int ival0 = (in0).to_int();
        int ival1 = (in1).to_int();
            
        int outi = ival0 + ival1;

        return sc_lv<DOUT_WIDTH>(outi);
       }
       else return sc_lv<DOUT_WIDTH>(SC_LOGIC_X);
    }

};

template<int ID, int NUM_STAGE,
         int DIN0_WIDTH, int DIN1_WIDTH=DIN0_WIDTH,
         int DOUT_WIDTH=DIN0_WIDTH>
struct ACMP_sub_comb :
    public AESLCFUComp<NUM_STAGE, DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>
{
public:

    ACMP_sub_comb(const char* mname) :
        AESLCFUComp<NUM_STAGE, DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>(mname) {
        sanity_check();
    }

    virtual void sanity_check() {
        //assert(DIN0_WIDTH == 32);
        //assert(DIN0_WIDTH == DOUT_WIDTH);
    }

    virtual sc_lv<DOUT_WIDTH>
    compute(const sc_lv<DIN0_WIDTH>& in0, const sc_lv<DIN1_WIDTH>& in1) {
        int ival0 = (in0).to_int();
        int ival1 = (in1).to_int();

        int outi = ival0 - ival1;

        return sc_lv<DOUT_WIDTH>(outi);
    }

};

template<int ID, int NUM_STAGE,
         int DIN0_WIDTH, int DIN1_WIDTH=DIN0_WIDTH,
         int DOUT_WIDTH=DIN0_WIDTH>
struct ACMP_sub :
    public AESLFUComp<NUM_STAGE, DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>
{
public:

    ACMP_sub(const char* mname) :
        AESLFUComp<NUM_STAGE, DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>(mname) {
        sanity_check();
    }

    virtual void sanity_check() {
        //assert(DIN0_WIDTH == 32);
        //assert(DIN0_WIDTH == DOUT_WIDTH);
    }

    virtual sc_lv<DOUT_WIDTH>
    compute(const sc_lv<DIN0_WIDTH>& in0, const sc_lv<DIN1_WIDTH>& in1) {
        int ival0 = (in0).to_int();
        int ival1 = (in1).to_int();

        int outi = ival0 - ival1;

        return sc_lv<DOUT_WIDTH>(outi);
    }

};

template<int ID, int NUM_STAGE,
         int DIN0_WIDTH, int DIN1_WIDTH=DIN0_WIDTH,
         int DOUT_WIDTH=DIN0_WIDTH> 
struct ACMP_urem_comb :
    public AESLCFUComp<NUM_STAGE, DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>
{
public:

    ACMP_urem_comb(const char* mname) :
        AESLCFUComp<NUM_STAGE, DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>(mname) {
        sanity_check();
    }
         
    virtual void sanity_check() {
        //  assert(DIN0_WIDTH == 32);
        //   assert(DIN1_WIDTH == DOUT_WIDTH);
    }
    
    virtual sc_lv<DOUT_WIDTH>
    compute(const sc_lv<DIN0_WIDTH>& in0, const sc_lv<DIN1_WIDTH>& in1) {
    	if (!in0.is_01() || !in1.is_01()) return sc_lv<DOUT_WIDTH>();
        unsigned int ival0 = (in0).to_uint();
        unsigned int ival1 = (in1).to_uint();
            
        unsigned int outi;
        if (ival1 != 0)  {
            outi = ival0 % ival1;
        } 
        else {
            outi = 0;
        }

        return sc_lv<DOUT_WIDTH>(outi);
    }

};


template<int ID, int NUM_STAGE,
         int DIN0_WIDTH, int DIN1_WIDTH=DIN0_WIDTH,
         int DOUT_WIDTH=DIN0_WIDTH> 
struct ACMP_urem :
    public AESLFUComp<NUM_STAGE, DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>
{
public:

    ACMP_urem(const char* mname) :
        AESLFUComp<NUM_STAGE, DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>(mname) {
        sanity_check();
    }
         
    virtual void sanity_check() {
        //  assert(DIN0_WIDTH == 32);
        //   assert(DIN1_WIDTH == DOUT_WIDTH);
    }
    
    virtual sc_lv<DOUT_WIDTH>
    compute(const sc_lv<DIN0_WIDTH>& in0, const sc_lv<DIN1_WIDTH>& in1) {
    	if (!in0.is_01() || !in1.is_01()) return sc_lv<DOUT_WIDTH>();
        unsigned int ival0 = (in0).to_uint();
        unsigned int ival1 = (in1).to_uint();
            
        unsigned int outi;
        if (ival1 != 0)  {
            outi = ival0 % ival1;
        } 
        else {
            outi = 0;
        }

        return sc_lv<DOUT_WIDTH>(outi);
    }

};

template<int ID, int NUM_STAGE,
         int DIN0_WIDTH, int DIN1_WIDTH=DIN0_WIDTH,
         int DOUT_WIDTH=DIN0_WIDTH> 
struct ACMP_srem_comb :
    public AESLCFUComp<NUM_STAGE, DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>
{
public:

    ACMP_srem_comb(const char* mname) :
        AESLCFUComp<NUM_STAGE, DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>(mname) {
        sanity_check();
    }
         
    virtual void sanity_check() {
        // assert(DIN0_WIDTH == 32);
        // assert(DIN1_WIDTH == DOUT_WIDTH);
    }
    
    virtual sc_lv<DOUT_WIDTH>
    compute(const sc_lv<DIN0_WIDTH>& in0, const sc_lv<DIN1_WIDTH>& in1) {
    	if (!in0.is_01() || !in1.is_01()) return sc_lv<DOUT_WIDTH>();
        if (DIN0_WIDTH > 32)
        {
            long long ival0 = (in0).to_uint64();
            long long ival1 = (in1).to_uint64();
            
            long long outi;
            if (ival1 != 0)  {
                outi = ival0 % ival1;
            } 
            else {
                outi = 0x0;
            }
            return sc_lv<DOUT_WIDTH>(outi);
        } else {
            int ival0 = (in0).to_int();
            int ival1 = (in1).to_int();
                
            int outi;
            if (ival1 != 0)  {
                outi = ival0 % ival1;
            } 
            else {
                outi = 0x0;
            }
            return sc_lv<DOUT_WIDTH>(outi);
        }

    }

};


template<int ID, int NUM_STAGE,
         int DIN0_WIDTH, int DIN1_WIDTH=DIN0_WIDTH,
         int DOUT_WIDTH=DIN0_WIDTH> 
struct ACMP_srem :
    public AESLFUComp<NUM_STAGE, DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>
{
public:

    ACMP_srem(const char* mname) :
        AESLFUComp<NUM_STAGE, DIN0_WIDTH, DIN1_WIDTH, DOUT_WIDTH>(mname) {
        sanity_check();
    }
         
    virtual void sanity_check() {
        // assert(DIN0_WIDTH == 32);
        // assert(DIN1_WIDTH == DOUT_WIDTH);
    }
    
    virtual sc_lv<DOUT_WIDTH>
    compute(const sc_lv<DIN0_WIDTH>& in0, const sc_lv<DIN1_WIDTH>& in1) {
    	if (!in0.is_01() || !in1.is_01()) return sc_lv<DOUT_WIDTH>();
        if (DIN0_WIDTH > 32)
        {
            long long ival0 = (in0).to_uint64();
            long long ival1 = (in1).to_uint64();
            
            long long outi;
            if (ival1 != 0)  {
                outi = ival0 % ival1;
            } 
            else {
                outi = 0x0;
            }
            return sc_lv<DOUT_WIDTH>(outi);
        } else {
            int ival0 = (in0).to_int();
            int ival1 = (in1).to_int();
                
            int outi;
            if (ival1 != 0)  {
                outi = ival0 % ival1;
            } 
            else {
                outi = 0x0;
            }
            return sc_lv<DOUT_WIDTH>(outi);
        }

    }

};



#endif 

